//
//  MyDocument.m
//  Earthinizer
//
//  Created by markd on 5/23/09.
//  Copyright Borkware 2009 . All rights reserved.
//

#import "BWTrackDocument.h"

#import "BWMapWindowController.h"


@interface NSAppleEventDescriptor (BWDoubleValueExtension)
- (double)doubleForKeyword:(AEKeyword)keyword;
@end // BWDoubleValueExtension


@implementation NSAppleEventDescriptor (BWDoubleValueExtension)

- (double)doubleForKeyword:(AEKeyword)keyword {
  NSAppleEventDescriptor *valAE;
  valAE = [self descriptorForKeyword:keyword];
  NSString *valString = [valAE stringValue];
  double val = [valString doubleValue];

  return val;

} // doubleForKeyword

@end // BWDoubleValueExtension


@implementation BWTrackDocument

@synthesize coordinates = coordinates_;
@synthesize kmlFileName = kmlFileName_;
@synthesize placeName = placeName_;
@synthesize lineColor = lineColor_;
@synthesize lineWidth = lineWidth_;
@synthesize tilt = tilt_;
@synthesize heading = heading_;
@synthesize range = range_;
@synthesize lookAtLat = lookAtLat_;
@synthesize lookAtLng = lookAtLng_;
@synthesize tourSpeed = tourSpeed_;
@synthesize followPath = followPath_;
@synthesize reversePath = reversePath_;
@synthesize mapWindowController = mapWindowController_;

- (id)init {
    self = [super init];
    if (self) {
        // Bootstrappage
        self.kmlFileName = @"track.kml";
        self.placeName = @"Track";
        self.tilt = 38.0;
        self.heading = -35.0;
        self.range = 2000.0;
        self.lineColor = [NSColor redColor];
        self.lineWidth = 3.0;
        self.tourSpeed = 0.05;
        self.followPath = YES;
    }
    return self;
} // init


- (NSString *)windowNibName {
    return @"BWTrackDocument";
} // windowNibName


- (void)windowControllerDidLoadNib:(NSWindowController *) aController {
    [super windowControllerDidLoadNib:aController];
    // Do awake-from-nib stuff

} // windowControllerDidLoadNib


- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)error {
    if (error) *error = NULL;

    NSMutableData *data = [[NSMutableData alloc] init];

    NSKeyedArchiver *archiver;
    archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [archiver encodeObject:self.coordinates forKey:@"coordinates"];
    [archiver encodeObject:self.kmlFileName forKey:@"kmlFileName"];
    [archiver encodeObject:self.placeName forKey:@"placeName"];
    [archiver encodeObject:self.lineColor forKey:@"lineColor"];
    [archiver encodeDouble:self.lineWidth forKey:@"lineWidth"];
    [archiver encodeDouble:self.tilt forKey:@"tilt"];
    [archiver encodeDouble:self.heading forKey:@"heading"];
    [archiver encodeDouble:self.range forKey:@"range"];
    [archiver encodeDouble:self.lookAtLat forKey:@"lookAtLat"];
    [archiver encodeDouble:self.lookAtLng forKey:@"lookAtLng"];
    [archiver encodeDouble:self.tourSpeed forKey:@"tourSpeed"];
    [archiver encodeBool:self.followPath forKey:@"followPath"];
    [archiver encodeBool:self.reversePath forKey:@"reversePath"];
    [archiver finishEncoding];

    return (data);

} // dataOfType


- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
               error:(NSError **)error {
    if (error) *error = NULL;

    NSKeyedUnarchiver *archiver;
    archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    self.coordinates = [archiver decodeObjectForKey:@"coordinates"];
    self.kmlFileName = [archiver decodeObjectForKey:@"kmlFileName"];
    self.placeName = [archiver decodeObjectForKey:@"placeName"];
    self.lineColor = [archiver decodeObjectForKey:@"lineColor"];
    self.lineWidth = [archiver decodeDoubleForKey:@"lineWidth"];
    self.tilt = [archiver decodeDoubleForKey:@"tilt"];
    self.heading = [archiver decodeDoubleForKey:@"heading"];
    self.range = [archiver decodeDoubleForKey:@"range"];
    self.lookAtLat = [archiver decodeDoubleForKey:@"lookAtLat"];
    self.lookAtLng = [archiver decodeDoubleForKey:@"lookAtLng"];
    self.tourSpeed = [archiver decodeDoubleForKey:@"tourSpeed"];
    self.followPath = [archiver decodeBoolForKey:@"followPath"];
    self.reversePath = [archiver decodeBoolForKey:@"reversePath"];

    return (YES);

} // readFromData


// --------------------------------------------------
// Render me, baby

- (NSString *)lineColorAsString {
    
    CGFloat red, green, blue, alpha;
    [self.lineColor getRed:&red green:&green blue:&blue alpha:&alpha];

    NSString *colorRep = [NSString stringWithFormat:@"%02x%02x%02x%02x",
                                   (int)(alpha * 255),
                                   (int)(blue * 255),
                                   (int)(green * 255),
                                   (int)(red * 255)];
    return colorRep;

} // lineColorAsString


#define _(x) [NSString stringWithFormat:@"%f", (x)]

- (NSArray *)rendererArgs {
    NSMutableArray *array =
      [NSMutableArray arrayWithObjects:
                      @"-n", self.placeName,
                      @"-c", [self lineColorAsString],
                      @"-w", _(self.lineWidth),
                      @"-t", _(self.tilt),
                      @"-h", _(self.heading),
                      @"-r", _(self.range),
                      @"-s", _(self.tourSpeed),
                      @"-f", self.followPath ? @"YES" : @"NO",
                      @"-R", self.reversePath ? @"YES" : @"NO",
                      nil];
    if (!self.followPath) {
      NSString *latlng = [NSString stringWithFormat:@"%f,%f",
                                   self.lookAtLat, self.lookAtLng];
      [array addObject: @"-l"];
      [array addObject: latlng];
    }
    return (array);
} // rendererArgs

#undef _

- (NSString *)renderedFilePath {
    NSString *home = NSHomeDirectory();
    NSString *path = [home stringByAppendingPathComponent:self.kmlFileName];

    return (path);

} // renderedFilePath


- (void)renderKML {
    // The make-tour.py script reads a single line of all the coordinates,
    // and then barfs out the KML.

    // Make sure all text field editing is comitted.
    [[self windowForSheet] makeFirstResponder:nil];
    
    // Find the script.
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *scriptPath = [bundle pathForAuxiliaryExecutable:@"make-tour.py"];
    NSData *coordData =
        [self.coordinates dataUsingEncoding:NSUTF8StringEncoding];

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:scriptPath];

    NSArray *args = [self rendererArgs];
    [task setArguments:args];

    NSPipe *taskInPipe = [NSPipe pipe];
    [task setStandardInput:taskInPipe];
    NSFileHandle *taskIn = [taskInPipe fileHandleForWriting];

    NSPipe *taskOutPipe = [NSPipe pipe];
    [task setStandardOutput:taskOutPipe];
    NSFileHandle *taskOut = [taskOutPipe fileHandleForReading];

    [task launch];
    [taskIn writeData:coordData];
    [taskIn closeFile];

    NSData *kmlData = [taskOut readDataToEndOfFile];
    NSString *kml = [[NSString alloc] initWithData:kmlData
                                      encoding:NSUTF8StringEncoding];
    NSError *error;
    BOOL result;
    NSString *path = [self renderedFilePath];
    result = [kml writeToFile:path
                  atomically:YES
                  encoding:NSUTF8StringEncoding
                  error:&error];

    if (!result) {
        NSLog(@"oopsie! %@", error);
    }
} // renderKML


- (void)openKML {
    NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
    [workspace openFile:[self renderedFilePath]];
} // openKML


- (IBAction)generateKML:(id)sender {
    [self renderKML];
    [self openKML];
} // generateKML


- (void)updateMap {
    if (self.mapWindowController) {
        [self.mapWindowController showRoute:self.coordinates
             withColor:self.lineColor];
    }
} // updateMap

// --------------------------------------------------
// Load Coordinates

- (void)loadCoordinatesFromString:(NSString *)kml
                           append:(BOOL)append {
    NSError *error = NULL;
    NSXMLDocument *doc =
        [[NSXMLDocument alloc] initWithXMLString:kml
                               options:0
                               error:&error];
    if (error) NSLog(@"ERRORZ %@", error);

    NSXMLNode *root = [doc rootElement];

    NSArray *nodes;
    nodes = [root nodesForXPath:@"//coordinates" error:&error];

    if (!append) {
        self.coordinates = @"";
    }
    
    for (id node in nodes) {
        NSString *coords = [node stringValue];
        if (coords) {
            if (!self.coordinates) self.coordinates = @"";
            self.coordinates =
                [self.coordinates stringByAppendingString:coords];
        }
    }

    [self updateMap];

} // loadCoordinatesFromString


- (void)useCoordinateOpenPanelDidEnd:(NSOpenPanel *) sheet
                          returnCode:(int) returnCode
                         contextInfo:(void *) context {
    if (returnCode == NSOKButton) {
        NSArray *fileNames = [sheet filenames];
        NSString *path = [fileNames objectAtIndex:0];
        NSString *kml = [[NSString alloc] initWithContentsOfFile:path
                                          encoding:NSUTF8StringEncoding
                                          error:NULL];
        
        [self loadCoordinatesFromString:kml
              append:NO];
    }

} // openPanelDidEnd


- (IBAction)useCoordinateFile:(id)sender {
    // Put up the sheet
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    
    NSWindow *window = [self windowForSheet];

    [panel beginSheetForDirectory:nil
           file:nil
           types:[NSArray arrayWithObject:@"kml"]
           modalForWindow:window
           modalDelegate:self
           didEndSelector:
               @selector(useCoordinateOpenPanelDidEnd:returnCode:contextInfo:)
           contextInfo:nil];

} // useCoordinateFile


- (NSString *)kmlFromClipboard {
    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
    NSData *data = [pasteboard dataForType:NSStringPboardType];
    NSString *kml = [[NSString alloc] initWithData:data
                                      encoding:NSUTF8StringEncoding];
    
    return kml;

} // kmlFromClipboard


- (IBAction)useCoordinatesFromClipboard:(id)sender {
    NSString *kml = [self kmlFromClipboard];
    [self loadCoordinatesFromString:kml
          append:NO];

} // useCoordinatesFromClipboard


- (IBAction)appendCoordinatesFromClipboard:(id)sender {
    NSString *kml = [self kmlFromClipboard];

    [self loadCoordinatesFromString:kml
          append:YES];

} // appendCoordinatesFromClipboard


- (IBAction)useViewFromEarth:(id)sender {

  NSAppleScript *dpo = [[NSAppleScript alloc]
                         initWithSource:
                           @"tell application \"Google Earth Pro\"\n"
                         "return GetViewInfo\n"
                         "end tell"];
  NSDictionary *dict = nil;
  if (![dpo compileAndReturnError:&dict]) {
    NSLog(@"FAIL: %@", dict);
  }

  NSAppleEventDescriptor *stuff;
  stuff = [dpo executeAndReturnError:&dict];
  if (!stuff) {
    NSLog(@"EPIC FAIL: %@", dict);
  }

  double dist = [stuff doubleForKeyword:'Edis'];
  double tilt = [stuff doubleForKeyword:'Etil'];
  double azi = [stuff doubleForKeyword:'Eazi'];

  double lat = [stuff doubleForKeyword:'Elat'];
  double lng = [stuff doubleForKeyword:'Elon'];

  self.tilt = tilt;
  self.range = dist;
  self.heading = azi;
  self.lookAtLat = lat;
  self.lookAtLng = lng;

} // useViewFromEarth


- (IBAction)showMap:(id)sender {
    if (!self.mapWindowController) {
        self.mapWindowController =
            [[BWMapWindowController alloc]
                initWithWindowNibName:@"BWMapWindowController"];
        self.mapWindowController.document = self;
        [self.mapWindowController showWindow:self];
        // Gotta wait til the map is loaded, otherwise the 'updateMap' won't
        // actually work.
        [self performSelector:@selector(updateMap)
              withObject:nil
              afterDelay:3.0];
    } else {
        [self.mapWindowController showWindow:self];
        [self updateMap];
    }

} // showMap


- (IBAction)clear:(id)sender {
    self.coordinates = nil;
} // clear


- (IBAction)reversePath:(id)sender {
  self.reversePath = [sender state] == NSOnState;
}

- (void) savePanelDidEnd: (NSSavePanel *) sheet
              returnCode: (int) returnCode
             contextInfo: (void *) context {
    NSString *profile = (NSString *) context;

    if (returnCode == NSOKButton) {
        NSString *filename =
            [[sheet filename] stringByAppendingPathExtension:@"csv"];
        [profile writeToFile: filename
                 atomically: YES
                 encoding: NSUTF8StringEncoding
                 error: NULL];
    }
    [profile release];
}
    

- (IBAction) generateProfile: (id) sender {
    // The make-tour.py script reads a single line of all the coordinates,
    // and then barfs out the profile.

    // Make sure all text field editing is comitted.
    [[self windowForSheet] makeFirstResponder:nil];

    NSBundle *bundle = [NSBundle mainBundle];
    NSString *scriptPath =
        [bundle pathForAuxiliaryExecutable:@"make-profile.py"];
    NSData *coordData =
        [self.coordinates dataUsingEncoding:NSUTF8StringEncoding];

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:scriptPath];

    NSArray *args = [NSArray arrayWithObjects: @"-R", @"NO", nil];
    [task setArguments:args];

    NSPipe *taskInPipe = [NSPipe pipe];
    [task setStandardInput:taskInPipe];
    NSFileHandle *taskIn = [taskInPipe fileHandleForWriting];

    NSPipe *taskOutPipe = [NSPipe pipe];
    [task setStandardOutput:taskOutPipe];
    NSFileHandle *taskOut = [taskOutPipe fileHandleForReading];

    [task launch];
    [taskIn writeData:coordData];
    
    [taskIn closeFile];

    NSData *profileData = [taskOut readDataToEndOfFile];
    NSString *profile = [[NSString alloc] initWithData: profileData
                                          encoding: NSUTF8StringEncoding];
    
    NSSavePanel *panel = [NSSavePanel savePanel];

    [panel beginSheetForDirectory: nil
           file: nil
           modalForWindow: [self windowForSheet]
           modalDelegate: self
           didEndSelector: @selector(savePanelDidEnd:returnCode:contextInfo:)
           contextInfo: profile];
}

@end
